/* Copyright (c) 2004-2022 peigen.info. All rights reserved. */

package info.peigen.hotpot.business.customer.test.base;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import info.peigen.hotpot.business.customer.core.domain.Customer;
import info.peigen.hotpot.business.customer.core.domain.partner.Partner;
import info.peigen.hotpot.business.customer.core.domain.partner.action.PartnerConverter;
import info.peigen.hotpot.business.customer.core.repository.entity.CustomerOperatorEntity;
import info.peigen.hotpot.business.customer.core.repository.entity.CustomerTechEntity;
import info.peigen.hotpot.business.customer.core.repository.entity.PartnerEntity;
import info.peigen.hotpot.business.customer.core.service.CustomerQueryService;
import info.peigen.hotpot.business.customer.core.service.CustomerService;
import info.peigen.hotpot.business.customer.core.service.PartnerQueryService;
import info.peigen.hotpot.business.customer.core.service.PartnerService;
import info.peigen.hotpot.business.customer.facade.enums.CustomerStatus;
import info.peigen.hotpot.business.customer.facade.enums.partner.PartnerStatus;
import info.peigen.hotpot.business.customer.facade.info.CustomerInfo;
import info.peigen.hotpot.business.customer.facade.info.partner.PartnerInfo;
import info.peigen.hotpot.business.customer.facade.order.login.CustomerLoginOrder;
import info.peigen.hotpot.business.customer.facade.order.partner.PartnerApplyPermissionOrder;
import info.peigen.hotpot.business.customer.facade.order.partner.PartnerCreateOrder;
import info.peigen.hotpot.business.customer.facade.order.register.CustomerRegisterOrder;
import info.peigen.hotpot.business.customer.facade.order.update.CustomerUpdateOrder;
import info.peigen.hotpot.business.customer.facade.result.partner.PartnerApplyPermissionResult;
import info.peigen.hotpot.business.customer.facade.result.partner.PartnerCreateResult;
import info.peigen.hotpot.business.customer.facade.result.register.CustomerRegisterResult;
import info.peigen.hotpot.common.core.result.ResultCode;
import info.peigen.hotpot.common.core.result.ResultStatus;
import info.peigen.hotpot.common.service.base.test.ServiceTestContext;
import info.peigen.hotpot.common.service.base.test.ServiceTestInvoke;
import info.peigen.hotpot.common.service.base.test.mock.ServiceTestMockContext;
import info.peigen.hotpot.common.service.base.test.mock.ServiceTestMockContextThreadLocal;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import org.junit.jupiter.api.Assertions;
import org.noear.solon.annotation.Inject;

/**
 * <b>(CustomerServiceTestBase)</b>
 *
 * @author Peigen
 * @version 1.0.0
 * @since 2021/7/31
 */
@SuppressWarnings({"resource", "UnusedReturnValue"})
@FieldDefaults(level = AccessLevel.PROTECTED)
public class CustomerServiceTestBase extends CustomerRepositoryTestBase {
    @Inject
    CustomerService      customerService;
    @Inject
    PartnerService       partnerService;
    @Inject
    PartnerQueryService  partnerQueryService;
    @Inject
    CustomerQueryService customerQueryService;

    protected String registerCustomer() {
        String partnerId = registerPartner();
        return registerCustomer(LOGIN_NAME, LOGIN_PASSWORD, LOGIN_PASSWORD, ResultStatus.success, ResultCode.SUCCESS, "");
    }

    protected String registerCustomer(String loginName, String loginPassword, String confirmPassword, ResultStatus resultStatus, ResultCode resultCode, String detail) {
        ServiceTestContext serviceTestContext = ServiceTestContext.builder().resultStatus(resultStatus).resultCode(resultCode).build();
        new ServiceTestMockContextThreadLocal(ServiceTestMockContext.builder().expectResultStatus(resultStatus).expectResultCode(resultCode).expectErrorCode(detail).build());

        executeTest(serviceTestContext, new ServiceTestInvoke() {
            @Override
            public void cleanData() {
                if (resultStatus == ResultStatus.success) {
                    cleanCustomerByLoginName(loginName);
                }
            }

            @Override
            public void initData(ServiceTestContext serviceTestContext) {
                serviceTestContext.setOrder(genCustomerRegisterOrder());
            }

            @Override
            public void callService(ServiceTestContext serviceTestContext) {
                CustomerRegisterOrder  order  = (CustomerRegisterOrder) serviceTestContext.getOrder();
                CustomerRegisterResult result = customerService.register(order);
                serviceTestContext.setSingleResult(result);
            }

            @Override
            public void dataAssert(ServiceTestContext serviceTestContext) {
                ServiceTestInvoke.super.dataAssert(serviceTestContext);
                CustomerRegisterResult result   = (CustomerRegisterResult) serviceTestContext.getSingleResult();
                Customer               customer = customerRepository.active(result.getInfo().getCustomerId());
                Assertions.assertNotNull(customer);
                Assertions.assertSame(customer.getStatus(), CustomerStatus.Un_Cert);
                Assertions.assertNotNull(customer.getCustomerBase());
                Assertions.assertNotNull(customer.getCustomerTech());
                Assertions.assertNotNull(customer.getOperators());
                Assertions.assertNotNull(customer.getMasterOperator());
                Assertions.assertNotNull(customer.getPermission());
            }
        });
        return ((CustomerInfo) serviceTestContext.getSingleResult().getInfo()).getCustomerId();
    }

    protected CustomerRegisterOrder genCustomerRegisterOrder() {
        return genCustomerRegisterOrder(LOGIN_NAME, LOGIN_PASSWORD, LOGIN_PASSWORD, SELF_PARTNER_ID);
    }

    protected CustomerRegisterOrder genCustomerRegisterOrder(String loginName, String loginPassword, String confirmPassword, String partnerId) {
        return CustomerRegisterOrder.builder()
                .loginName(StrUtil.isNotEmpty(loginName) ? loginName : LOGIN_NAME)
                .loginPassword(StrUtil.isNotEmpty(loginPassword) ? loginPassword : LOGIN_PASSWORD)
                .confirmPassword(StrUtil.isNotEmpty(confirmPassword) ? confirmPassword : LOGIN_PASSWORD)
                .partnerId(StrUtil.isNotEmpty(partnerId) ? partnerId : SELF_PARTNER_ID)
                .build();
    }

    protected CustomerLoginOrder genCustomerLoginOrder() {
        return genCustomerLoginOrder(LOGIN_NAME, LOGIN_PASSWORD, VERIFICATION_CODE, LOGIN_IP, SELF_PARTNER_ID);
    }

    protected CustomerLoginOrder genCustomerLoginOrder(String loginName, String loginPassword, String verificationCode, String loginIp, String partnerId) {
        return CustomerLoginOrder.builder()
                .loginName(StrUtil.isNotEmpty(loginName) ? loginName : LOGIN_NAME)
                .loginPassword(StrUtil.isNotEmpty(loginPassword) ? loginPassword : LOGIN_PASSWORD)
                .verificationCode(StrUtil.isNotEmpty(verificationCode) ? verificationCode : VERIFICATION_CODE)
                .loginIp(StrUtil.isNotEmpty(loginIp) ? loginIp : LOGIN_IP)
                .partnerId(StrUtil.isNotEmpty(partnerId) ? partnerId : SELF_PARTNER_ID)
                .build();
    }

    protected CustomerUpdateOrder genCustomerUpdateMobileOrder() {
        return genCustomerUpdateMobileOrder(LOGIN_NAME, LOGIN_PASSWORD, MOBILE_NO);
    }

    protected CustomerUpdateOrder genCustomerUpdateMobileOrder(String loginName, String loginPassword, String mobile) {
        return CustomerUpdateOrder.builder()
                .loginName(StrUtil.isNotEmpty(loginName) ? loginName : LOGIN_NAME)
                .loginPassword(StrUtil.isNotEmpty(loginPassword) ? loginPassword : LOGIN_PASSWORD)
                .mobile(StrUtil.isNotEmpty(mobile) ? mobile : MOBILE_NO)
                .build();
    }

    protected CustomerUpdateOrder genCustomerUpdateAvatarAndNicknameOrder() {
        return genCustomerUpdateAvatarAndNicknameOrder(LOGIN_NAME, LOGIN_PASSWORD, DEFAULT_AVATAR, CUSTOMER_NICK_NAME);
    }

    protected CustomerUpdateOrder genCustomerUpdateAvatarAndNicknameOrder(String loginName, String loginPassword, String avatar, String nickName) {
        return CustomerUpdateOrder.builder()
                .loginName(StrUtil.isNotEmpty(loginName) ? loginName : LOGIN_NAME)
                .loginPassword(StrUtil.isNotEmpty(loginPassword) ? loginPassword : LOGIN_PASSWORD)
                .avatar(StrUtil.isNotEmpty(avatar) ? avatar : NEW_AVATAR)
                .nickname(StrUtil.isNotEmpty(nickName) ? nickName : CUSTOMER_NICK_NAME)
                .build();
    }

    protected void cleanCustomerByCustomerId(String customerId) {
        if (StrUtil.isNotEmpty(customerId)) {
            customerMapper.deleteByCustomerId(customerId);
            customerOperatorMapper.deleteByCustomerId(customerId);
            customerAuthenticationMapper.deleteByCustomerId(customerId);
            customerAuthenticationBaseMapper.deleteByCustomerId(customerId);
            customerAuthenticationLogMapper.deleteByCustomerId(customerId);
            customerTechMapper.deleteByCustomerId(customerId);
            accountRepository.getMapper().deleteByCustomerId(customerId);
        }
    }

    protected void cleanCustomerByLoginName(String loginName) {
        CustomerOperatorEntity entity = customerRepository.getCustomerOperatorRepository().getMapper().findByLoginName(loginName);
        if (ObjectUtil.isNotEmpty(entity) && StrUtil.isNotEmpty(entity.getCustomerId())) {
            cleanCustomerByCustomerId(entity.getCustomerId());
        }
    }

    protected void cleanCustomerByOpenId(String openId) {
        CustomerTechEntity techEntity = customerRepository.getCustomerTechRepository().getMapper().findByOpenId(openId);

        if (ObjectUtil.isNotNull(techEntity)) {
            cleanCustomerByCustomerId(techEntity.getCustomerId());
        }
    }

    // partner
    protected PartnerCreateOrder genPartnerCreateOrder() {
        return PartnerCreateOrder.builder()
                .partnerId(SELF_PARTNER_ID)
                .abbreviation(SELF_ABBREVIATION)
                .bizOrderNo(RandomUtil.randomString(20))
                .build();
    }

    protected String registerPartner() {
        return registerPartner(PARTNER_NAME);
    }

    protected String registerPartner(String partnerName) {
        return registerPartner(partnerName, ResultStatus.success, ResultCode.SUCCESS, "");
    }

    protected String registerPartner(String partnerName, ResultStatus resultStatus, ResultCode resultCode, String detail) {
        ServiceTestContext serviceTestContext = ServiceTestContext.builder().resultStatus(resultStatus).resultCode(resultCode).build();
        new ServiceTestMockContextThreadLocal(ServiceTestMockContext.builder().expectResultStatus(resultStatus).expectResultCode(resultCode).expectErrorCode(detail).build());

        executeTest(serviceTestContext, new ServiceTestInvoke() {
            @Override
            public void cleanData() {
                if (resultStatus == ResultStatus.success) {
                    cleanPartnerByName(partnerName);
                }
            }

            @Override
            public void initData(ServiceTestContext serviceTestContext) {
                serviceTestContext.setOrder(genPartnerCreateOrder().setPartnerId(SELF_PARTNER_ID).setPartnerName(partnerName).setAbbreviation(SELF_ABBREVIATION));
            }

            @Override
            public void callService(ServiceTestContext serviceTestContext) {
                PartnerCreateOrder  order  = (PartnerCreateOrder) serviceTestContext.getOrder();
                PartnerCreateResult result = partnerService.register(order);
                serviceTestContext.setSingleResult(result);
                partnerApplyPermission(partnerName);
            }

            @Override
            public void dataAssert(ServiceTestContext serviceTestContext) {
                ServiceTestInvoke.super.dataAssert(serviceTestContext);
                PartnerCreateResult result  = (PartnerCreateResult) serviceTestContext.getSingleResult();
                Partner             partner = partnerRepository.active(result.getInfo().getPartnerId());
                Assertions.assertNotNull(partner);
                Assertions.assertSame(partner.getStatus(), PartnerStatus.NORMAL);
                Assertions.assertNotNull(partner.getSelfAccount());
                Assertions.assertNotNull(partner.getIntermediateAccount());
                Assertions.assertNotNull(partner.getPermission());
            }
        });
        return ((PartnerInfo) serviceTestContext.getSingleResult().getInfo()).getPartnerId();
    }

    protected void partnerApplyPermission() {
        partnerApplyPermission(PARTNER_NAME);
    }

    protected void partnerApplyPermission(String partnerName) {
        partnerApplyPermission(partnerName, ResultStatus.success, ResultCode.SUCCESS, "");
    }

    public void partnerApplyPermission(String partnerName, ResultStatus resultStatus, ResultCode resultCode, String detail) {
        ServiceTestContext serviceTestContext = ServiceTestContext.builder().resultStatus(resultStatus).resultCode(resultCode).build();
        new ServiceTestMockContextThreadLocal(ServiceTestMockContext.builder().expectResultStatus(resultStatus).expectResultCode(resultCode).expectErrorCode(detail).build());

        executeTest(serviceTestContext, new ServiceTestInvoke() {
            @Override
            public void cleanData() {
            }

            @Override
            public void initData(ServiceTestContext serviceTestContext) {
                PartnerEntity partnerEntity = partnerRepository.getMapper().findByPartnerName(partnerName);
                serviceTestContext.setDomain(PartnerConverter.convert(partnerEntity));

                PartnerApplyPermissionOrder order = PartnerApplyPermissionOrder.builder().partnerId(partnerEntity.getPartnerId()).enableApiCodes(API_CODES).build();
                serviceTestContext.setOrder(order);
            }

            @Override
            public void callService(ServiceTestContext serviceTestContext) {
                PartnerApplyPermissionOrder  order  = (PartnerApplyPermissionOrder) serviceTestContext.getOrder();
                PartnerApplyPermissionResult result = partnerService.applyPermission(order);
                serviceTestContext.setSingleResult(result);
            }

            @Override
            public void dataAssert(ServiceTestContext serviceTestContext) {
                ServiceTestInvoke.super.dataAssert(serviceTestContext);
                PartnerApplyPermissionResult result  = (PartnerApplyPermissionResult) serviceTestContext.getSingleResult();
                Partner                      partner = partnerRepository.active(result.getInfo().getPartnerId());
                Assertions.assertNotNull(partner);
                Assertions.assertSame(partner.getStatus(), PartnerStatus.NORMAL);
                Assertions.assertTrue(CollUtil.isNotEmpty(partner.getPermission().getEnableApiCodes()));
            }
        });
    }

}